﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using ORM.Business.Feedbacks;
using ORM.Business.Interfaces;
using ORM.Business.Models;
using ORM.Business.Models.Entities;
using ORM.Common.Logging;
using ORM.Resources;

namespace ORM.Business.Repositories
{
    public class AnswerRepository : IAnswerRepository
    {
        public AnswerListFeedback GetAllAnswers(int currentUserId, int questionId, PageInfo pageInfo)
        {
            try
            {
                using(var ORMDbContext = new OrmEntities())
                {
                    var answers = new List<Answer>();
                    var query = (from a in ORMDbContext.Answers
                                 join u2 in ORMDbContext.Users on a.PostedById equals u2.Id
                                 join q in ORMDbContext.Questions on a.QuestionId equals q.Id
                                 orderby a.PostedDate descending
                                 where !a.IsDeleted && !q.IsDeleted && a.QuestionId == questionId && a.IsApproved
                                 select new
                                 {
                                     AnswerId = a.Id,
                                     AnswerContentHtml = a.ContentHtml,
                                     AnswerPostedById = a.PostedById,
                                     AnswerPostedDate = a.PostedDate,
                                     AnswerDownVotes = a.DownVotes,
                                     AnswerUpVotes = a.UpVotes,
                                     AnswerIsAccepted = a.IsAccepted,
                                     AnswerUsername = u2.Username,
                                     AnswerAvatarUrl = u2.AvatarUrl,
                                     a.IsReadByQuestioner,
                                 });

                    var question = ORMDbContext.Questions.FirstOrDefault(t => t.Id == questionId);
                    if (question == null)
                        return new AnswerListFeedback(false, Messages.QuestionNotExisted);
                    var total = query.Count();

                    query = query.Skip((pageInfo.PageNo - 1) * pageInfo.PageSize).Take(pageInfo.PageSize);
                    foreach (var q in query)
                    {
                        var answerVote = ORMDbContext.VotedAnswers.SingleOrDefault(t => t.AnswerId == q.AnswerId && t.VotedById == currentUserId);
                        bool isVotedByCurrentUser = answerVote != null;
                        bool highLight = false;
                        if(currentUserId == question.PostedById && currentUserId != q.AnswerPostedById)
                        {
                            highLight = !q.IsReadByQuestioner;
                            var answer = ORMDbContext.Answers.Find(q.AnswerId);
                            answer.IsReadByQuestioner = true;
                        }
                        answers.Add(new Answer
                        {
                            Id = q.AnswerId,
                            ContentHtml = q.AnswerContentHtml,
                            PostedById = q.AnswerPostedById,
                            PostedDate = q.AnswerPostedDate,
                            PostedBy = new user { Id = q.AnswerPostedById, Username = q.AnswerUsername, AvatarUrl = q.AnswerAvatarUrl },
                            QuestionId = questionId,
                            UpVotes = q.AnswerUpVotes,
                            DownVotes = q.AnswerDownVotes,
                            IsAccepted = q.AnswerIsAccepted,
                            IsVotedByCurrentUser = isVotedByCurrentUser,
                            Highlight = highLight,
                        });
                    }
                    ORMDbContext.SaveChanges();
                    return new AnswerListFeedback(true, null, answers, total);
                }
                

            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new AnswerListFeedback(false, Messages.GeneralError);
            }
        }
        public AnswerListFeedback GetHighScoreAnswers(int currentUserId, int questionId, PageInfo pageInfo)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    var answers = new List<Answer>();
                    var query = (from a in ORMDbContext.Answers
                                 join u2 in ORMDbContext.Users on a.PostedById equals u2.Id
                                 join q in ORMDbContext.Questions on a.QuestionId equals q.Id
                                 orderby a.UpVotes descending
                                 where !a.IsDeleted && !q.IsDeleted && a.QuestionId == questionId && a.IsApproved
                                 select new
                                 {
                                     AnswerId = a.Id,
                                     AnswerContentHtml = a.ContentHtml,
                                     AnswerPostedById = a.PostedById,
                                     AnswerPostedDate = a.PostedDate,
                                     AnswerDownVotes = a.DownVotes,
                                     AnswerUpVotes = a.UpVotes,
                                     AnswerIsAccepted = a.IsAccepted,
                                     AnswerUsername = u2.Username,
                                     AnswerAvatarUrl = u2.AvatarUrl,
                                 });

                    var question = ORMDbContext.Questions.FirstOrDefault(t => t.Id == questionId);
                    if (question == null)
                        return new AnswerListFeedback(false, Messages.QuestionNotExisted);
                    var total = query.Count();

                    query = query.Skip((pageInfo.PageNo - 1) * pageInfo.PageSize).Take(pageInfo.PageSize);
                    foreach (var q in query)
                    {
                        var answerVote = ORMDbContext.VotedAnswers.SingleOrDefault(t => t.AnswerId == q.AnswerId && t.VotedById == currentUserId);
                        bool isVotedByCurrentUser = answerVote != null;
                        answers.Add(new Answer
                        {
                            Id = q.AnswerId,
                            ContentHtml = q.AnswerContentHtml,
                            PostedById = q.AnswerPostedById,
                            PostedDate = q.AnswerPostedDate,
                            PostedBy = new user { Id = q.AnswerPostedById, Username = q.AnswerUsername, AvatarUrl = q.AnswerAvatarUrl },
                            QuestionId = questionId,
                            UpVotes = q.AnswerUpVotes,
                            DownVotes = q.AnswerDownVotes,
                            IsAccepted = q.AnswerIsAccepted,
                            IsVotedByCurrentUser = isVotedByCurrentUser,
                        });
                    }

                    return new AnswerListFeedback(true, null, answers, total);
                }


            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new AnswerListFeedback(false, Messages.GeneralError);
            }
        }


        public AnswerListFeedback GetAcceptedAnswer(int currentUserId, int questionId, PageInfo pageInfo)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    var answers = new List<Answer>();
                    var query = (from a in ORMDbContext.Answers
                                 join u2 in ORMDbContext.Users on a.PostedById equals u2.Id
                                 join q in ORMDbContext.Questions on a.QuestionId equals q.Id
                                 orderby a.UpVotes descending
                                 where !a.IsDeleted && !q.IsDeleted && a.QuestionId == questionId && a.IsAccepted && a.IsApproved
                                 select new
                                 {
                                     AnswerId = a.Id,
                                     AnswerContentHtml = a.ContentHtml,
                                     AnswerPostedById = a.PostedById,
                                     AnswerPostedDate = a.PostedDate,
                                     AnswerDownVotes = a.DownVotes,
                                     AnswerUpVotes = a.UpVotes,
                                     AnswerIsAccepted = a.IsAccepted,
                                     AnswerUsername = u2.Username,
                                     AnswerAvatarUrl = u2.AvatarUrl,
                                 });

                    var question = ORMDbContext.Questions.FirstOrDefault(t => t.Id == questionId);
                    if (question == null)
                        return new AnswerListFeedback(false, Messages.QuestionNotExisted);
                    var total = query.Count();

                    query = query.Skip((pageInfo.PageNo - 1) * pageInfo.PageSize).Take(pageInfo.PageSize);
                    foreach (var q in query)
                    {
                        var answerVote = ORMDbContext.VotedAnswers.SingleOrDefault(t => t.AnswerId == q.AnswerId && t.VotedById == currentUserId);
                        bool isVotedByCurrentUser = answerVote != null;
                        answers.Add(new Answer
                        {
                            Id = q.AnswerId,
                            ContentHtml = q.AnswerContentHtml,
                            PostedById = q.AnswerPostedById,
                            PostedDate = q.AnswerPostedDate,
                            PostedBy = new user { Id = q.AnswerPostedById, Username = q.AnswerUsername, AvatarUrl = q.AnswerAvatarUrl },
                            QuestionId = questionId,
                            UpVotes = q.AnswerUpVotes,
                            DownVotes = q.AnswerDownVotes,
                            IsAccepted = q.AnswerIsAccepted,
                            IsVotedByCurrentUser = isVotedByCurrentUser,
                        });
                    }

                    return new AnswerListFeedback(true, null, answers, total);
                }


            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new AnswerListFeedback(false, Messages.GeneralError);
            }
        }

        public Feedback<Answer> InsertAnswer(Answer a)
        {
            var answer = new Answer
            {
                QuestionId = a.QuestionId,
                ContentHtml = a.ContentHtml,
                PostedById = a.PostedById,
                PostedDate = DateTime.Now,
                LastUpdate = null,
                UpdatedBy = null,
                IsAccepted = false,
                IsDeleted = false,
                IsApproved = false,
                IsReadByQuestioner = false,
            };
            try
            {
                    using(var ORMDbContext = new OrmEntities())
                    {
                    // Add to DB
                    ORMDbContext.Answers.Add(answer);
                    ORMDbContext.SaveChanges();
                    var user = ORMDbContext.Users.Where(t => t.Id == answer.PostedById).Select(t => new { t.Id, t.AvatarUrl, t.Username }).First();
                    answer.PostedBy = new user { Id = user.Id, AvatarUrl = user.AvatarUrl, Username = user.Username };
                return new Feedback<Answer>(true, null, answer);
                    }
                    
            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback<Answer>(false, Messages.GeneralError);
            }
        }
        public Feedback<Answer> VoteAnswer(int answerId, int userId)
        {
            try
            {
                using(var ORMDbContext = new OrmEntities())
                {
                    var answer = ORMDbContext.Answers.Single(i => i.Id == answerId);
                    var answerVote = ORMDbContext.VotedAnswers.SingleOrDefault(t => t.AnswerId == answerId && t.VotedById == userId);
                    var User = ORMDbContext.Users.Find(answer.PostedById);
                    //Do not allow seft voting
                    if (answer.PostedById == userId)
                    {
                        return new Feedback<Answer>(false, Messages.VoteAnswer_SeftVoting, null);
                    }

                    //Already voted up --> Remove this vote
                    if (answerVote != null)
                    {
                        ORMDbContext.VotedAnswers.Remove(answerVote);
                        answer.UpVotes--;
                        User.Ponit -= KeyObject.PointConfig.Like;
                    }
                    //Not vote yet (Insert)
                    else
                    {
                        answerVote = new VotedAnswer()
                        {
                            IsUpVote = true,
                            AnswerId = answerId,
                            VotedById = userId,
                            VotedDate = DateTime.Now,
                        };
                        ORMDbContext.VotedAnswers.Add(answerVote);
                        answer.UpVotes++;
                        User.Ponit += KeyObject.PointConfig.Like;
                    }

                    ORMDbContext.SaveChanges();

                    return new Feedback<Answer>(true, null, answer);
                }
                    
            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback<Answer>(false, Messages.GeneralError);
            }
        }
        public Feedback<Answer> MarkAnswerAsAccepted(int answerId)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    var answer = ORMDbContext.Answers.Single(i => i.Id == answerId);
                    answer.IsAccepted = true;
                    //Plus point for user
                    user user = ORMDbContext.Users.Find(answer.PostedById);
                    user.Ponit += KeyObject.PointConfig.AnswerIsAccepted;
                    ORMDbContext.SaveChanges();
                    return new Feedback<Answer>(true, null, answer);
                }

            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback<Answer>(false, Messages.GeneralError);
            }
        }

        public Feedback<Answer> DeleteAnAnswer(int answerId)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    var answer = ORMDbContext.Answers.Single(i => i.Id == answerId);
                    answer.IsDeleted = true;
                    if(answer.IsApproved)
                    {
                        var question = ORMDbContext.Questions.Single(t => t.Id == answer.QuestionId);
                        question.Answers--;
                        if(answer.IsAccepted)
                        {
                            answer.IsAccepted = false;
                            question.IsResolved = false;
                        }
                    }
                    
                    ORMDbContext.SaveChanges();
                    return new Feedback<Answer>(true, null, answer);
                }

            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback<Answer>(false, Messages.GeneralError);
            }
        }
        public Feedback<IList<Answer>> GetAllUnapprovedAnswers()
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    var answersList = new List<Answer>();
                    var query = (from a in ORMDbContext.Answers
                                 join u2 in ORMDbContext.Users on a.PostedById equals u2.Id
                                 join q in ORMDbContext.Questions on a.QuestionId equals q.Id
                                 orderby a.PostedDate ascending 
                                 where !a.IsDeleted && !q.IsDeleted && a.IsApproved == false && a.QuestionId == q.Id
                                 select new
                                 {
                                     AnswerId = a.Id,
                                     AnswerContentHtml = a.ContentHtml,
                                     AnswerPostedById = a.PostedById,
                                     AnswerPostedDate = a.PostedDate,
                                     AnswerDownVotes = a.DownVotes,
                                     AnswerUpVotes = a.UpVotes,
                                     AnswerIsAccepted = a.IsAccepted,
                                     AnswerUsername = u2.Username,
                                     AnswerAvatarUrl = u2.AvatarUrl,
                                     a.QuestionId,

                                 });

                    foreach (var q in query)
                    {
                        answersList.Add(new Answer
                        {
                            Id = q.AnswerId,
                            ContentHtml = q.AnswerContentHtml,
                            PostedById = q.AnswerPostedById,
                            PostedDate = q.AnswerPostedDate,
                            PostedBy = new user { Id = q.AnswerPostedById, Username = q.AnswerUsername, AvatarUrl = q.AnswerAvatarUrl },
                            QuestionId = q.QuestionId,
                            UpVotes = q.AnswerUpVotes,
                            DownVotes = q.AnswerDownVotes,
                        });
                    }

                    return new Feedback<IList<Answer>>(true, null, answersList);
                }


            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback<IList<Answer>>(false, Messages.GeneralError);
            }
        }

        public Feedback<IList<Answer>> GetAllApprovedAnswers()
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    var answersList = new List<Answer>();
                    var query = (from a in ORMDbContext.Answers
                                 join u2 in ORMDbContext.Users on a.PostedById equals u2.Id
                                 join q in ORMDbContext.Questions on a.QuestionId equals q.Id
                                 orderby a.PostedDate ascending
                                 where !a.IsDeleted && !q.IsDeleted && a.IsApproved && a.QuestionId == q.Id
                                 select new
                                 {
                                     AnswerId = a.Id,
                                     AnswerContentHtml = a.ContentHtml,
                                     AnswerPostedById = a.PostedById,
                                     AnswerPostedDate = a.PostedDate,
                                     AnswerDownVotes = a.DownVotes,
                                     AnswerUpVotes = a.UpVotes,
                                     AnswerIsAccepted = a.IsAccepted,
                                     AnswerUsername = u2.Username,
                                     AnswerAvatarUrl = u2.AvatarUrl,
                                     a.QuestionId,

                                 });

                    foreach (var q in query)
                    {
                        answersList.Add(new Answer
                        {
                            Id = q.AnswerId,
                            ContentHtml = q.AnswerContentHtml,
                            PostedById = q.AnswerPostedById,
                            PostedDate = q.AnswerPostedDate,
                            PostedBy = new user { Id = q.AnswerPostedById, Username = q.AnswerUsername, AvatarUrl = q.AnswerAvatarUrl },
                            QuestionId = q.QuestionId,
                            UpVotes = q.AnswerUpVotes,
                            DownVotes = q.AnswerDownVotes,
                        });
                    }

                    return new Feedback<IList<Answer>>(true, null, answersList);
                }


            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback<IList<Answer>>(false, Messages.GeneralError);
            }
        }
        public Feedback ApproveAnswer(int id)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    Answer answer = ORMDbContext.Answers.Single(i => i.Id == id);
                    answer.IsApproved = true;
                    var question = ORMDbContext.Questions.Single(t => t.Id == answer.QuestionId);
                    question.Answers++;
                    //Plus point for user
                    user user = ORMDbContext.Users.Find(answer.PostedById);
                    user.Ponit += KeyObject.PointConfig.CreateAnswer;
                    ORMDbContext.SaveChanges();

                    return new Feedback(true);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback(false, Messages.GeneralError);
            }
        }
        public Feedback DisapproveAnswer(int id)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    Answer answer = ORMDbContext.Answers.Single(i => i.Id == id);
                    answer.IsApproved = false;
                    answer.IsDeleted = true;                   
                    ORMDbContext.SaveChanges();
                    return new Feedback(true);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback(false, Messages.GeneralError);
            }
        }
        public Feedback UnapproveAnswer(int id)
        {
            try
            {
                using (var ORMDbContext = new OrmEntities())
                {
                    Answer a = ORMDbContext.Answers.Single(i => i.Id == id);
                    a.IsApproved = false;
                    var question = ORMDbContext.Questions.Single(t => t.Id == a.QuestionId);
                    question.Answers--;
                    ORMDbContext.SaveChanges();

                    return new Feedback(true);
                }
            }
            catch (Exception e)
            {
                Logger.Error(e);
                return new Feedback(false, Messages.GeneralError);
            }
        }
    }
}